#ifndef _MATHEMATICS_ALGEBRA_INTERVAL_
#define _MATHEMATICS_ALGEBRA_INTERVAL_

#include "math_helper.h"
#include "..\..\utilities\string_helper.h"
using namespace science::utilities;

#include <iostream>
#include <math.h>

namespace science
{
	namespace mathematics
	{
		namespace algebra
		{
			class interval
			{
			private:
				double _start;
				double _end;
				bool _start_opened;
				bool _end_opened;

			public:
				interval()
				{
					_start = 0; _end = 0;
					_start_opened = false;
					_end_opened = false;
				}

				interval(std::string interval_str)
				{
					unsigned int index = 0;
					*this = parse_interval(interval_str, index);
				}

				interval(double start, double end, bool start_opened = false, bool end_opened = false)
				{
					_start = start; _end = end;
					_start_opened = start_opened;
					_end_opened = end_opened;
				}

				~interval()
				{					
				}

			public:
				double get_start() { return _start; }
				double get_end() { return _end; }
				bool get_start_opened() { return _start_opened; }
				bool get_end_opened() { return _end_opened; }
				void set_start(double start) { _start = start; }
				void set_end(double end) { _end = end; }
				void set_start_opened(bool start_opened) { _start_opened = start_opened; }
				void set_end_opened(bool end_opened) { _end_opened = end_opened; }

			public:
				friend std::ostream &operator<<(std::ostream &output, const interval &inter)
				{
					if (math_helper::double_check(inter._start, -DBL_MAX) == 0 || inter._start_opened)
						output << "(";
					else if (!inter._start_opened)
						output << "[";
					if (math_helper::double_check(inter._start, -DBL_MAX) == 0)
						output << "-Inf,";
					else
						output << inter._start << ",";

					if (math_helper::double_check(inter._end, DBL_MAX) == 0)
						output << "Inf)";
					else
					{
						output << inter._end;
						if (inter._end_opened)
							output << ")";
						else
							output << "]";
					}
					return output;
				}

				friend std::istream &operator>>(std::istream &input, interval &inter)
				{
					double start = 0, end = 0;
					int start_type = 0, end_type = 0;
					input >> start >> end >> start_type >> end_type;

					inter._start = start;
					inter._end = end;
					inter._start_opened = start_type == 0 ? true : false;
					inter._end_opened = end_type == 0 ? true : false;

					return input;
				}

			public:
				static interval parse_interval(std::string str, unsigned int& index)
				{
					str = string_helper::remove_white_chars(str);
					interval inter;

					if (index >= str.size())
						return inter;

					if (str[index] == '(')
						inter.set_start_opened(true);
					else if (str[index] == '[')
						inter.set_start_opened(false);
					else
						throw new std::invalid_argument("Invalid interval string");
					index++;

					if (index >= str.size())
						throw new std::invalid_argument("Invalid interval string");

					inter.set_start(string_helper::parse_double(str, index));

					if (index >= str.size())
						throw new std::invalid_argument("Invalid interval string");

					if (str[index] == ',')
						index++;
					else
						throw new std::invalid_argument("Invalid interval string");
					inter.set_end(string_helper::parse_double(str, index));

					if (index >= str.size())
						throw new std::invalid_argument("Invalid interval string");

					if (str[index] == ')')
						inter.set_end_opened(true);
					else if (str[index] == ']')
						inter.set_end_opened(false);
					else
						throw new std::invalid_argument("Invalid interval string");
					index++;
					return inter;
				}
			};
		}
	}
}

#endif